#ifndef __POPUP_SERVER_HPP__
#define __POPUP_SERVER_HPP__

#include <string>
#include <PopupServerUI.hpp>
#include <PopupOSAL.hpp>
#include <msg/PopupMessages.hpp>
#include "PopupSSL.hpp"
#include "PopupDatabaseUI.hpp"

namespace Popup
{
  struct ClientSession {

    PopupUtils::Socket *socket;
    UserID id;
    bool registered;
    SslContext sslContext;
    MessageNetworker networker;
    User *user;
    bool disconnect;

    ClientSession(PopupUtils::Socket *p_socket,
                  RSAHelper *p_rsaHelper)
    : socket(p_socket), id(0), registered(false),
      sslContext(SERVER_CONTEXT, p_rsaHelper), networker(p_socket, &sslContext),
      user(0), disconnect(false) {}

    ~ClientSession() {
      delete(socket);
    }
  };

  class ServerImpl : public Server
  {
  public:
    virtual ~ServerImpl();
    static ServerImpl *createInstance(unsigned short p_port,
                                      unsigned short p_maxNbConnections,
                                      DatabaseUI *p_database,
                                      ServerUI *p_serverUI);
    static ServerImpl *getInstance();
    void run();

  private:
    // Constructor is private according to singleton pattern
    // Use PopupServer::getInstance instead.
    ServerImpl(unsigned short p_port,
               unsigned short p_maxNbConnections,
               DatabaseUI *p_database,
               ServerUI *p_serverUI);

    PopupUtils::Socket *serverSocket;
    static ServerImpl *instance;
    DatabaseUI *database;
    ServerUI *ui;
    bool exit;
    RSAHelper rsahelper;

    typedef std::map<UserID, ClientSession*> Sessions;
    typedef std::map<PopupUtils::OSSocketFd, ClientSession*> Sockets;

    Sessions sessions;
    Sockets sockets;
    UserLoginMap loginsMap;

  private:

    static void signalHandler(int p_signal);

    void initSessions();
    void acceptNewClient();
    void deleteSession(ClientSession *p_session);
    bool processActivity(ClientSession *p_session);
    void kill();
    void closeAllConnections();
    void broadcastMessage(AbstractMessage *p_message,
                          const ClientSession *p_sender = 0);
    void broadcastMessage(const RawMessage *p_message,
                          unsigned p_messageSize,
                          const ClientSession *p_from = 0);
    bool sendAllUsersList(ClientSession *p_session);
    void sendMessage(const RawMessage *p_message,
                     unsigned p_messageSize,
                     const UserList & p_targets);
  };
}
#endif // __POPUP_SERVER_HPP__
